﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;

namespace CoreVirtuoso.Helpers
{
   internal static class GeoHelper
   {
      public static string GetFilter(double westLatitude, double eastLatitude, double southLongitude,
                                     double northLongitude)
      {
         string geoFilter = "";

         //if (westLatitude < eastLatitude)
         //   geoFilter += "bif:st_y(?geo) >= {0} && bif:st_y(?geo) <= {1}";
         //else
         //   geoFilter += "({0} <= bif:st_y(?geo) && bif:st_y(?geo) <= 180 || bif:st_y(?geo) >= -180 && bif:st_y(?geo) <= {1})";
         geoFilter += "?lat >= {0} && ?lat <= {1}";

         //if (southLongitude < northLongitude)
         //   geoFilter += " && bif:st_x(?geo) >= {2} && bif:st_x(?geo) <= {3}";
         //else
         //   geoFilter += "&& ({2} <= bif:st_x(?geo) && bif:st_x(?geo) <= 85 || bif:st_x(?geo) >= -85 && bif:st_x(?geo) <= {3})";

         geoFilter += " && ?long >= {2} && ?long <= {3}";

         geoFilter = "bif:st_intersects (bif:st_point(?long, ?lat), bif:st_point ({5}, {4}), {6}) && " + geoFilter;

         // Really GetDistance()/2 will be enough. But because of infelicity it would be better to twice the radius

         return String.Format(geoFilter,
                              westLatitude.ToString(CultureInfo.InvariantCulture),
                              eastLatitude.ToString(CultureInfo.InvariantCulture),
                              southLongitude.ToString(CultureInfo.InvariantCulture),
                              northLongitude.ToString(CultureInfo.InvariantCulture),
                              ((westLatitude + eastLatitude)/2).ToString(CultureInfo.InvariantCulture),
                              ((southLongitude + northLongitude)/2).ToString(CultureInfo.InvariantCulture),
                              GetDistance(westLatitude, southLongitude, eastLatitude, northLongitude).
                              ToString(CultureInfo.InvariantCulture));
      }

      public static double GetDistance(double Lat1,
                                       double Long1, double Lat2, double Long2)
      {
         double dDistance = Double.MinValue;
         double dLat1InRad = Lat1*(Math.PI/180.0);
         double dLong1InRad = Long1*(Math.PI/180.0);
         double dLat2InRad = Lat2*(Math.PI/180.0);
         double dLong2InRad = Long2*(Math.PI/180.0);

         double dLongitude = dLong2InRad - dLong1InRad;
         double dLatitude = dLat2InRad - dLat1InRad;

         // Intermediate result a.

         double a = Math.Pow(Math.Sin(dLatitude/2.0), 2.0) +
                    Math.Cos(dLat1InRad)*Math.Cos(dLat2InRad)*
                    Math.Pow(Math.Sin(dLongitude/2.0), 2.0);

         // Intermediate result c (great circle distance in Radians).

         double c = 2.0*Math.Atan2(Math.Sqrt(a), Math.Sqrt(1.0 - a));

         // Distance.

         // const Double kEarthRadiusMiles = 3956.0;

         const Double kEarthRadiusKms = 6376.5;
         dDistance = kEarthRadiusKms*c;

         return dDistance;
      }
   }
}